﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.TeamFoundation.Migration.Toolkit.Wit
{
    /// <summary>
    /// Field comparer class is used to compare two sets of fields and detect mismatches
    /// between them.
    /// </summary>
    class FieldComparer
    {
        List<InternalFieldDefinition> m_fields;             // Conflicting fields
        List<Pair<object>> m_values;                        // Conflicting values

        /// <summary>
        /// Gets the collection of conflicting fields.
        /// </summary>
        public IList<InternalFieldDefinition> ConflictingFields { get { return m_fields; } }

        /// <summary>
        /// Gets the collection of conflicting values.
        /// </summary>
        public IList<Pair<object>> ConflictingValues { get { return m_values;  } }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="type">Work item type both fields belong to</param>
        /// <param name="values">Fields' values</param>
        public FieldComparer(
            InternalWorkItemType type,
            Pair<IEnumerable<MigrationField>> values)
        {
            m_fields = new List<InternalFieldDefinition>();
            m_values = new List<Pair<object>>();

            // Load values on the left
            Side tfsSide = type.SharedData.TfsSide;
            var leftVals = new Dictionary<string, object>(type.SharedData.Stores[tfsSide].FieldNameComparer);
            foreach (MigrationField field in values.Left)
            {
                var def = type.GetFieldDefinition(field.Name, Side.Left);
                if (def != null)
                {
                    leftVals.Add(def.Names[tfsSide], field.Value);
                }
            }

            // Load values on the right
            var rightVals = new Dictionary<string, object>(leftVals.Comparer);
            SystemType rightSystem = type.SharedData.TfsSide == Side.Right ? SystemType.Tfs : SystemType.Other;
            foreach (MigrationField field in values.Right)
            {
                var def = type.GetFieldDefinition(field.Name, Side.Right);
                if (def != null)
                {
                    rightVals.Add(
                        def.Names[tfsSide], 
                        def.TranslateValue(field.Value, type.SharedData.StoreTypes.Right));
                }
            }

            // Detect conflicts
            foreach (KeyValuePair<string, object> leftItem in leftVals)
            {
                object leftValue = leftItem.Value;
                object rightValue;

                if (rightVals.TryGetValue(leftItem.Key, out rightValue))
                {
                    if (!type.SharedData.TfsValueComparer.Equals(leftValue, rightValue))
                    {
                        AddConflict(type, leftItem.Key, leftValue, rightValue);
                    }
                }
                else
                {
                    AddConflict(type, leftItem.Key, leftValue, null);
                }
            }

            foreach (KeyValuePair<string, object> rightItem in rightVals)
            {
                if (!leftVals.ContainsKey(rightItem.Key))
                {
                    AddConflict(type, rightItem.Key, null, rightItem.Value);
                }
            }
        }

        /// <summary>
        /// Adds a field conflict.
        /// </summary>
        /// <param name="type">Work item type the field belongs to</param>
        /// <param name="tfsFieldName">Field name in the TFS system</param>
        /// <param name="leftValue">Value on the left side</param>
        /// <param name="rightValue">Value on the right side</param>
        private void AddConflict(
            InternalWorkItemType type,
            string tfsFieldName, 
            object leftValue, 
            object rightValue)
        {
            var def = type.GetFieldDefinition(tfsFieldName, type.SharedData.TfsSide);
            Debug.Assert(def != null, "Field definition not found!");

            m_fields.Add(def);
            // Value on the right is already normalized. Normalize value on the left.
            leftValue = def.TranslateValue(leftValue, type.SharedData.StoreTypes.Left);
            m_values.Add(new Pair<object>(leftValue, rightValue));
        }
    }
}
